﻿using System;
using System.Linq;
using System.Threading.Tasks;
using K9Nano.LayIM.Events;
using K9Nano.LayIM.Models;
using MediatR;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace K9Nano.LayIM.Hubs
{
    public class ChatHub : Hub
    {
        private readonly ILogger _logger;
        private readonly IChatGroupProvider _chatGroupProvider;
        private readonly IMediator _mediator;
        private readonly LayIMOptions _options;
        private readonly IJsonSerializer _jsonSerializer;

        public ChatHub(ILogger<ChatHub> logger,
            IMediator mediator,
            IChatGroupProvider chatGroupProvider,
            IOptions<LayIMOptions> options,
            IJsonSerializer jsonSerializer)
        {
            _logger = logger;
            _mediator = mediator;
            _chatGroupProvider = chatGroupProvider;
            _jsonSerializer = jsonSerializer;
            _options = options.Value;
        }

        public async Task SendMessage(string eventName, string data)
        {
            var message = System.Text.Json.JsonSerializer.Deserialize<LayIMSendMessage>(data);

            // todo 待重构
            if (eventName == Constants.Event_Chat)
            {
                if (message.to.type == Constants.Chat_Type_Group)
                {
                    var response = new LayIMReceiveMessage
                    {
                        username = message.mine.username,
                        avatar = message.mine.avatar,
                        id = message.to.id,
                        type = message.to.type,
                        content = message.mine.content,
                        fromid = message.mine.id
                    };
                    await Clients.GroupExcept(message.to.id, this.Context.ConnectionId)
                        .SendAsync(Constants.Method_Receive_Message, eventName, _jsonSerializer.Serialize(response));
                    return;
                }

                if (message.to.type == Constants.Chat_Type_Friend)
                {
                    var response = new LayIMReceiveMessage
                    {
                        username = message.mine.username,
                        avatar = message.mine.avatar,
                        id = message.mine.id,
                        type = Constants.Chat_Type_Friend,
                        content = message.mine.content,
                        fromid = message.mine.id
                    };
                    await Clients.User(message.to.id)
                        .SendAsync(Constants.Method_Receive_Message, eventName, _jsonSerializer.Serialize(response));
                    return;
                }
            }
           
            _logger.LogWarning($"No handler for {eventName}: {data}");
        }

        public override async Task OnConnectedAsync()
        {
            var uid = this.Context.UserIdentifier;

            _logger.LogDebug($"User {uid} connected");

            var onlineNotification = new OnlineNotification(this.Context.ConnectionId, this.Context.User, _options);

            var groups = await _chatGroupProvider.GetGroupsAsync(onlineNotification);

            foreach (var group in groups)
            {
                await this.Groups.AddToGroupAsync(this.Context.ConnectionId, group);
            }
            
            await _mediator.Publish(onlineNotification);
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var uid = this.Context.UserIdentifier;

            _logger.LogDebug($"User {uid} disconnected");

            var roles = this.Context.User.FindAll(_options.RoleClaimType)
                .Select(x => x.Value)
                .ToArray();

            var offlineNotification = new OfflineNotification(this.Context.ConnectionId, this.Context.User, _options, exception?.Message);

            await _mediator.Publish(offlineNotification);
        }
    }
}